/**
 * 
 */
package org.stars.daostars.core.runtime;

import java.beans.MethodDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.stars.daostars.ParameterConverter;
import org.stars.daostars.core.DaoMap;
import org.stars.daostars.core.PropertyDescriptorEx;
import org.stars.daostars.core.SqlParameter;
import org.stars.daostars.core.SqlQuery;
import org.stars.daostars.core.SqlUtility;
import org.stars.daostars.core.Helper;
import org.stars.security.UserIdentity;
import org.stars.config.AppSettings;
import org.stars.util.StringUtility;

/**
 * Contiene delle utilità di base da utilizzare a Runtime
 * 
 * @author Francesco Benincasa (908099)
 * 
 */
public abstract class DaoRuntime {

	/**
	 * Restituisce il codice sql e l'elenco di parametri pronti per essere
	 * utilizzati.
	 * 
	 * @param parameterConverter
	 *            convertitore di parametri
	 * @param user
	 *            utente
	 * @param eb
	 *            entity bean usato come parametro
	 * @param indexedParameters
	 *            parametri indicizzati in input
	 * @param variables
	 *            variabili
	 * @param query
	 * @param moreSqlQuery
	 * @param orderQuery
	 * @return codice sql ed elenco parametri
	 * @throws Exception
	 *             in caso di errore
	 * 
	 * @see SimpleQuery
	 */
	public static SimpleQuery getSimpleQuery(UserIdentity user, Object eb, Object[] indexedParameters, Map<String, String> variables,
			SqlQuery query, SqlQuery moreSqlQuery, SqlQuery orderQuery) throws Exception {

		// user può essere null
		indexedParameters=(indexedParameters!=null)?indexedParameters: new Object[]{};
		variables=(variables!=null)?variables: new HashMap<String, String>();
		// query non deve essere nulla
		//moreSqlQuery può essere null
		// orderQuery viene controllato in seguito
		
		String conversionMapName=StringUtility.nvl(query.getConversionMapName());
		DaoMap	conversionMap=null;
		
		if (conversionMapName.length()>0)
		{
			conversionMap=query.getDefinition().getMap(conversionMapName);
		}
		
		String queryName = query.getName();
		SqlQuery[] queries = { query, moreSqlQuery };

		SimpleQuery ret = new SimpleQuery();

		StringBuffer sb = new StringBuffer();
		String tempSql;
		List<SqlParameter> tempP;
		List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();

		for (SqlQuery item : queries) {
			if (item != null) {
				tempSql = item.getSql(user, eb, indexedParameters, variables);
				tempP = item.getParameters(user, eb, indexedParameters, variables);
				sqlParameters.addAll(tempP);

				sb.append(tempSql);
			}
		}

		// aggiunge il metodo order by
		if (orderQuery != null) {
			tempSql = orderQuery.getSql(user, eb, indexedParameters, variables);

			tempSql = tempSql.toLowerCase();

			if (!tempSql.contains("order by")) {
				tempSql = " order by " + tempSql;
			}

			tempP = orderQuery.getParameters(user, eb, indexedParameters, variables);
			sqlParameters.addAll(tempP);

			// per sicurezza aggiungiamo uno spazio
			sb.append(" " + tempSql);
		}

		// codice sql
		ret.preparedSql = sb.toString();
		// parametri sql utilizzati.
		ret.preparedParameterNames = sqlParameters;

		PropertyDescriptorEx[] properties = Helper.convertFromGetter(eb.getClass());
		MethodDescriptor[] utilityMethod = Helper.methodDescriptors(SqlUtility.class);

		Object[] values = new Object[sqlParameters.size()];

		if (sqlParameters.size() > 0) {
			int[] indiciMetodi = DaoRuntimeHelper.mapAttributeToMethod(properties, sqlParameters, queryName);
			int[] indiciUtility = DaoRuntimeHelper.mapFunctionToMethod(utilityMethod, sqlParameters, queryName);

			int i = 0;
			PropertyDescriptorEx tempPD;
			Object temp;
			for (SqlParameter p : sqlParameters) {
				switch (p.getType()) {
				case TYPE_ATTRIBUTE:
					tempPD = properties[indiciMetodi[i]];					
					temp = tempPD.invokeGetter(eb);
															
					values[i] = temp;//parameterConverter.convert(tempPD.getName(), temp);
					
					if (conversionMap!=null)
					{
						values[i]=conversionMap.convertJavaToDatabase(tempPD.getName(), values[i]);
					}
					break;
				case TYPE_INDEX:
					temp = DaoRuntimeHelper.getValueInArray(indexedParameters, p.getParameterIndex());
					values[i] =  temp;//parameterConverter.convert(String.valueOf(p.getParameterIndex()), temp);
					
					if (conversionMap!=null)
					{
						values[i]=conversionMap.convertJavaToDatabase(String.valueOf(p.getParameterIndex()), values[i]);
					}
					break;
				case TYPE_RESOLVED:
					temp = p.getValue();
					values[i] = temp;//parameterConverter.convert(null, temp);
					
					if (conversionMap!=null)
					{
						values[i]=conversionMap.convertJavaToDatabase(p.getSimpleAttribute(), values[i]);
					}
					break;
				case TYPE_CONFIG_ATTRIBUTE:
					temp = AppSettings.getInstance().parameter(p.getAttribute());
					values[i] = temp;//parameterConverter.convert("config." + p.getAttribute(), temp);
					break;
				case TYPE_USER_ATTRIBUTE:
					temp = DaoRuntimeHelper.getUserAttribute(user, p.getAttribute());
					values[i] = temp;//parameterConverter.convert("user." + p.getAttribute(), temp);
					break;
				}

				if (indiciUtility[i] != -1) {
					temp = DaoRuntimeHelper.callStaticMethod(utilityMethod[indiciUtility[i]], new Object[] { values[i] });
					values[i] = temp;//parameterConverter.convert(null, temp);
				}
				i++;
			}
		}

		ret.preparedParameters = values;

		return ret;

	}

	/**
	 * Restituisce il codice sql e l'elenco di parametri pronti per essere
	 * utilizzati.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param parameterConverter
	 *            convertitore di parametri
	 * @param user
	 *            utente
	 * @param listEb
	 *            lista di entity bean usati come parametro
	 * @param indexedParameters
	 *            parametri indicizzati in input
	 * @param variables
	 *            variabili
	 * @param queries
	 *            insieme di query
	 * @return codice sql ed elenco parametri
	 * 
	 * @throws Exception
	 *             in caso di errore
	 */ 
	public static SimpleBatchQuery getSimpleBatchQuery(String queryName, ParameterConverter parameterConverter, UserIdentity user, Object[] listEb, Object[][] indexedParameters,
			Map<String, String> variables, SqlQuery... queries) throws Exception {

		// array dei parametri indicizzati relativi al parametro 0
		Object[] indexedParametersIndex0 = null;

		if (indexedParameters != null && indexedParameters.length > 0) {
			indexedParametersIndex0 = indexedParameters[0];
		}
		
		String conversionMapName=StringUtility.nvl(queries[0].getConversionMapName());
		DaoMap	conversionMap=null;
		
		if (conversionMapName.length()>0)
		{
			conversionMap=queries[0].getDefinition().getMap(conversionMapName);
		}

		int numQuery = listEb.length;
		int counter = 0;
		SimpleBatchQuery ret = new SimpleBatchQuery();
		ret.preparedParameters = new Object[numQuery][];

		StringBuffer sb = new StringBuffer();
		String tempSql;
		List<SqlParameter> tempP;
		List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();

		// definiamo il codice sql e il numero di parametri
		for (SqlQuery query : queries) {
			if (query != null) {
				tempSql = query.getSql(user, listEb[0], indexedParametersIndex0, variables);
				tempP = query.getParameters(user, listEb[0], indexedParametersIndex0, variables);
				sqlParameters.addAll(tempP);

				sb.append(tempSql);
			}
		}
		ret.preparedSql = sb.toString();

		// definiamo i descrittori
		PropertyDescriptorEx[] properties = Helper.convertFromGetter(listEb[0].getClass());
		MethodDescriptor[] utilityMethod = Helper.methodDescriptors(SqlUtility.class);

		int[] indiciMetodi = DaoRuntimeHelper.mapAttributeToMethod(properties, sqlParameters, queryName);
		int[] indiciUtility = DaoRuntimeHelper.mapFunctionToMethod(utilityMethod, sqlParameters, queryName);

		for (Object ebI : listEb) {
			Object[] values = new Object[sqlParameters.size()];

			if (sqlParameters.size() > 0) {
				int i = 0;
				PropertyDescriptorEx tempPD;
				Object temp;
				for (SqlParameter p : sqlParameters) {
					switch (p.getType()) {
					case TYPE_ATTRIBUTE:
						tempPD = properties[indiciMetodi[i]];
						//temp = Helper.callGetter(eb, tempPD);
						temp=tempPD.invokeGetter(ebI);
						values[i] = parameterConverter.convert(tempPD.getName(), temp);
						
						if (conversionMap!=null)
						{
							values[i]=conversionMap.convertJavaToDatabase(tempPD.getName(), values[i]);
						}
						
						break;
					case TYPE_INDEX:
						temp = DaoRuntimeHelper.getValueInArray(indexedParameters, p.getParameterIndex());
						values[i] = parameterConverter.convert(String.valueOf(p.getParameterIndex()), temp);
						
						if (conversionMap!=null)
						{
							values[i]=conversionMap.convertJavaToDatabase(String.valueOf(p.getParameterIndex()), values[i]);
						}
						
						break;
					case TYPE_RESOLVED:
						temp = p.getValue();
						values[i] = parameterConverter.convert(null, temp);
						
						if (conversionMap!=null)
						{
							values[i]=conversionMap.convertJavaToDatabase(p.getSimpleAttribute(), values[i]);
						}
						break;
					case TYPE_CONFIG_ATTRIBUTE:
						temp = AppSettings.getInstance().parameter(p.getAttribute());
						values[i] = parameterConverter.convert("config." + p.getAttribute(), temp);
						break;						
					case TYPE_USER_ATTRIBUTE:
						temp = DaoRuntimeHelper.getUserAttribute(user, p.getAttribute());
						values[i] = parameterConverter.convert("user." + p.getAttribute(), temp);
						break;
					}

					if (indiciUtility[i] != -1) {
						temp = DaoRuntimeHelper.callStaticMethod(utilityMethod[indiciUtility[i]], new Object[] { values[i] });
						values[i] = parameterConverter.convert(null, temp);
					}
					i++;
				}
			}
			ret.preparedParameters[counter++] = values;
		}

		return ret;

	}
}
